package com.gopay.cashier.web.controller.scan;

import com.alibaba.fastjson.JSONObject;
import com.gopay.cashier.common.exception.WebException;
import com.gopay.cashier.domain.bean.LoginUser;
import com.gopay.cashier.domain.bean.PayChannelDtlBean;
import com.gopay.cashier.domain.bean.PayChannelWayBean;
import com.gopay.cashier.service.riskcontrol.CallRiskControlService;
import com.gopay.cashier.web.command.ScanPayCommand;
import com.gopay.cashier.web.controller.BaseController;
import com.gopay.cashier.web.utils.CashierConfig;
import com.gopay.common.Result;
import com.gopay.common.constants.SubAccountConstants;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.constants.solution.PayChanExtConstants;
import com.gopay.common.constants.trans.*;
import com.gopay.common.constants.txncd.IntTxnCd;
import com.gopay.common.cps.dao.order.CpsGeneralReceiptOrderQueryDAO;
import com.gopay.common.cps.dao.trans.CpsReceiptSplitDtlQueryDAO;
import com.gopay.common.domain.acps.AcctStatus;
import com.gopay.common.domain.acps.FrezCode;
import com.gopay.common.domain.acps.model.AcctMastBean;
import com.gopay.common.domain.bank.MerchantModelInfo;
import com.gopay.common.domain.bank.ScanPayRequest;
import com.gopay.common.domain.bank.wechat.scan.res.ScanPayResultMsg;
import com.gopay.common.domain.cps.CpsGenMainOrder;
import com.gopay.common.domain.cps.CpsGenReceiptOrder;
import com.gopay.common.domain.cps.CpsReceiptSplitDtl;
import com.gopay.common.domain.pg.PgGenOrderTransDtl;
import com.gopay.common.domain.poundage.PoundageRes;
import com.gopay.common.domain.solution.SolutionResponse;
import com.gopay.common.exception.GopayException;
import com.gopay.common.util.DateUtils;
import com.gopay.common.util.FastJsonUtil;
import com.gopay.common.util.RequestUtils;
import com.gopay.remote.bank.ScanPayInteractRemoteService;
import com.gopay.remote.order.MainOrderField;
import com.gopay.remote.order.ReceiptOrderUpdateRemoteService;
import com.gopay.remote.solution.SolutionExtRemoteService;
import com.gopay.remote.solution.vo.PayChannelDtlInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 扫码支付
 * 
 * @author dongdh @2016/8/9 14:38 www.gopay.com.cn Inc.All rights reserved.
 */

public class ScanPayBaseController extends BaseController {

	protected static final String PORTAL_URL = CashierConfig.get(CashierConfig.PORTAL_URL);

	/**
	 * 数据为空的时候 网关会保存成[--]
	 **/
	protected static final String PG_NULL_FILED = "--";

	/**
	 * 二维码 失效时间
	 **/
	protected static long expiredTime = TransConstants.EXPIRETIME / 1000;

	/**
	 * 扫码订单过期时间点 key前缀 + orderId
	 */
	protected static String ORDER_EXPIRED_PREFIX = "ORDER_EXPIRED_PREFIX_";

	@Resource(name = "receiptOrderUpdateService")
	protected ReceiptOrderUpdateRemoteService receiptOrderUpdateService;

	@Autowired
	private CpsGeneralReceiptOrderQueryDAO cpsGeneralReceiptOrderQueryDAO;

	@Autowired
	private CpsReceiptSplitDtlQueryDAO cpsReceiptSplitDtlQueryDAO;
	/**
	 * 扫码支付远程接口
	 */
	@Resource(name = "scanPayRemoteServiceClient")
	protected ScanPayInteractRemoteService scanPayRemoteServiceClient;

	/**
	 * 方案扩展
	 */
	@Resource(name = "solutionExtClient")
	protected SolutionExtRemoteService solutionExtClient;

	/**
	 * 风控服务
	 */
	@Resource(name = "callRiskControlService")
	private CallRiskControlService callRiskControlService;


	/**
	 * 创建 扫码支付 bis报文
	 *
	 * @param order
	 * @return
	 */
	protected String buildScanPayBisMessage(CpsGenMainOrder order, ScanPayCommand command) throws GopayException {

		final PgGenOrderTransDtl dtl = pgGeneralOrderTransDtlQueryManager.get(order.getTransDtlSq());

		ScanPayRequest req = new ScanPayRequest();
		req.setOrderId(order.getGopayOrderId());
		if (StringUtils.equals(command.getBankCode(), BankConstants.BANK_LOGO_WECHAT)
				|| StringUtils.equals(command.getBankCode(), BankConstants.BANK_LOGO_ALIPAY)) {
			// 扫码支付 报文
			SolutionResponse resp = solutionExtClient.getPayChanExtVal(order.getMainRecvAcct(), IntTxnCd._00100.value,
					PayChannel._16.value, PayChanExtConstants.EXT_NAME_IS_OPEN_CREDIT_CARD);
			/**
			 * 如果是微信扫码业务执行是否支持一户一码模式
			 */
			String merchantModelRes = null;
			List<MerchantModelInfo> merchantModelInfo = null;

			if (BankConstants.BANK_LOGO_WECHAT.equals(command.getBankCode())||BankConstants.BANK_LOGO_ALIPAY.equals(command.getBankCode())) {
                SolutionResponse solMerchatResp;
                if(isSplit(order)){
                     solMerchatResp = solutionExtClient.getPayChanExtVal(order.getOtherAcct(), IntTxnCd._00100.value, PayChannel._16.value,
                            PayChanExtConstants.EXT_IS_OPEN_MERCHANTMODEL);
                }else{
                     solMerchatResp = solutionExtClient.getPayChanExtVal(order.getMainRecvAcct(), IntTxnCd._00100.value, PayChannel._16.value,
                            PayChanExtConstants.EXT_IS_OPEN_MERCHANTMODEL);
                }
				if (solMerchatResp.isSucceed()) {
					merchantModelRes = (String) solMerchatResp.getResponse();
					logger.info("方案返回json数据为：[" + merchantModelRes + "]");				
					 if(!"{}".equals(merchantModelRes)){
						 JSONObject object = (JSONObject) FastJsonUtil.toBean(merchantModelRes);
							merchantModelInfo = FastJsonUtil.toList(object.get("channelSolution").toString(),
									MerchantModelInfo.class);
		         		 }      
				}
			}

			if (resp.isSucceed()) {
				String isOpenCredit = (String) resp.getResponse();

				logger.info("是否支持信用卡：isOpenCredit=" + isOpenCredit);

				req.setLimitCreditPay(isOpenCredit);
				req.setBankBusProd(BisBankBusinessProd._16);
				req.setScanType(command.getBankCode());
				req.setMchCreateIp(clientIp(getRequest()));
				req.setMerchantModel(merchantModelInfo);
				if (StringUtils.isNotBlank(dtl.getGoodsInfo()) && !PG_NULL_FILED.equals(dtl.getGoodsInfo())) {
					req.setGoodsBody(dtl.getGoodsInfo()); // 商品信息
				} else if (StringUtils.isNotBlank(dtl.getGoodsName()) && !PG_NULL_FILED.equals(dtl.getGoodsName())) {
					req.setGoodsBody(dtl.getGoodsName());
				} else {
					req.setGoodsBody("国付宝订单：" + order.getGopayOrderId());
				}

				if (req.getGoodsBody().length() > 35) {
					/// 防止描述 过长
					req.setGoodsBody(req.getGoodsBody().substring(0, 35));
				}

				// 默认报文里没有这些 字段
				req.setDeviceInfo("");
				req.setGoodsTag("");
				req.setProductId("");

				logger.info("微信扫码支付请求msg::" + ToStringBuilder.reflectionToString(req));
				try {
					Result result = scanPayRemoteServiceClient.pay(req);

					if (result.isSuccess()) {
						ScanPayResultMsg resultMsg = (ScanPayResultMsg) result.getDefaultModel();
						logger.info("获取扫码imgUrl =" + resultMsg.getCode_img_url());
						logger.info("获取扫码codeUrl =" + resultMsg.getCode_url());

						if (StringUtils.isBlank(resultMsg.getCode_img_url())) {
							throw new GopayException("", "未取到二维码！");
						}

						 return resultMsg.getCode_url();

					} else {
						logger.error("微信预下单调bis调用错误：order =" + order.getGopayOrderId() + ",,erroeMsg ="
								+ result.getErrorMsg());
						throw new GopayException(result.getResultCode(), result.getErrorMsg());
					}
				} catch (GopayException e) {
					e.printStackTrace();
					throw e;
				}

			} else {
				logger.error("方案扩展调用异常.");
				throw new GopayException(ProcCodeConstants.PROC_CODE_100E9002);
			}
		} else {
			logger.error("不支持的bankCode=" + command.getBankCode());
			throw new GopayException(ProcCodeConstants.PROC_CODE_100E9002);
		}
	}

	/**
	 * 基础校验
	 *
	 * @param order
	 * @throws GopayException
	 */
	protected void validate(CpsGenMainOrder order, ScanPayCommand command, boolean isRefresh) throws GopayException {                       
		if (TxnStaCode.TXN_STA_CD_30000.value == order.getTxnStaCd()
				|| (TxnStaCode.TXN_STA_CD_30101.value == (order.getTxnStaCd())
						&& StringUtils.equals(PayChannel._16.value, order.getPayChannel())
						&& StringUtils.equals(command.getBankCode(), order.getOutStlmId()))) {
			if (isRefresh && TxnStaCode.TXN_STA_CD_30000.value == order.getTxnStaCd()) {
				// 如果是刷新 ，则状态应该30101
				logger.error("刷新二维码 订单装维应该为30101 ，订单状态不对！txn_sta_cd wrong!" + order);
				throw new WebException(ProcCodeConstants.PROC_CODE_100E5042);
			}
		} else {
			logger.error("订单状态不对！txn_sta_cd wrong!" + order);
			throw new WebException(ProcCodeConstants.PROC_CODE_100E5042);
		}

		if (StringUtils.isBlank(command.getBankCode()))
			throw new WebException(ProcCodeConstants.PROC_CODE_100E9002);

		// 校验收款方
		checkRecvCustStatus(2, order.getMainRecvCustId());
		AcctMastBean acct = acctTransService.getAccount(order.getMainRecvAcct());
		if (null == acct) {
			logger.error("账户不存在,acctId=" + order.getMainRecvAcct());
			throw new WebException(ProcCodeConstants.PROC_CODE_100E6012);
		}
		if (AcctStatus.F.equals(acct.getAccStatus())) {
			if (FrezCode.CRE.equals(acct.getFrezCode()) || FrezCode.FUL.equals(acct.getFrezCode())) {
				throw new GopayException(ProcCodeConstants.PROC_CODE_100E3025,
						"账户[" + order.getMainRecvAcct() + "]处于冻结状态[" + acct.getFrezCode() + "]，无法入款");
			}
		}
	}

	protected void riskControlCheck(CpsGenMainOrder order) throws GopayException {
		// 风控校验
		rimsProcess(order);
		// 风控服务 暂时屏蔽掉 待做支付宝时 统一开启
		// callRiskControlService.transCall(order.getGopayOrderId(),
		// order.getGopayIntTxnCd(), order.getPayChannel(),
		// order.getMerTxnAmt(), order.getMainRecvCustId());
	}

	/**
	 * 更新订单
	 *
	 * @param order
	 * @param command
	 * @param isRepeat
	 */
	protected void updateOrder(CpsGenMainOrder order, ScanPayCommand command, boolean isRepeat) throws WebException {

		Map<MainOrderField, String> values = new HashMap<MainOrderField, String>();
		LoginUser loginUser = getLoginUser();
		if (loginUser != null) {
			values.put(MainOrderField.PAY_CUST_ID, loginUser.getUser().getCustId());
			// values.put(MainOrderField.PAY_ACCT, command.getExGopayAcctId());
		} else {
			values.put(MainOrderField.PAY_CUST_ID, "");
		}

		values.put(MainOrderField.LAST_UPDATE_TIME, DateUtils.date2string(new Date(), DateUtils.DEFAULT_PATTERN));
		values.put(MainOrderField.REMARK2, order.getRemark2());

		/** 远程IP地址 songefengli 20160922 **/
		values.put(MainOrderField.REMOTE_IP, RequestUtils.getRemoteRealIpAddr(getRequest()));

		if (!isRepeat) {
			// 不是刷新订单 ， 订单第一次更新

			PayChannel payChannel = PayChannel._16;
			// 更新订单
			// 扫码支付增加特例银行编码 update by jianghoup
			logger.info("扫码支付特例银行编码：" + command.getBankCode());
			PoundageRes poundageRes = getPoundageRes(order, payChannel, command.getBankCode()); // 获取手续费

			values.put(MainOrderField.PAY_CHANNEL, payChannel.value);
			values.put(MainOrderField.OUT_STLM_ID, command.getBankCode());
			values.put(MainOrderField.ORDER_DEAL_AMT, poundageRes.getOrderDealAmt().toString());
			values.put(MainOrderField.ORDER_REAL_FEE_AMT, poundageRes.getOrderRealFeeAmt().toString());
			values.put(MainOrderField.TOTAL_ORDER_AMT, poundageRes.getTotalAmt().toString());
			values.put(MainOrderField.TOTAL_ORDER_FEE_AMT, poundageRes.getTotalFeeAmt().toString());
			values.put(MainOrderField.ORDER_FEE_PAYER, poundageRes.getOrderFeePayer().value);
			values.put(MainOrderField.TXN_STA_CD, TxnStaCode.TXN_STA_CD_30101.value + "");
			// 添加结算金额 add by jianglijun 20160926
			values.put(MainOrderField.SETTLE_AMT, poundageRes.getSettleAmt().toString());
			// 如果是收款方承担手续费，且手续费金额大于订单金额，中断交易，防止实际交易金额为负数。
			if (OrderFeePayer.REC_PAYER.equals(poundageRes.getOrderFeePayer())) {
				if (1 == poundageRes.getTotalFeeAmt().compareTo(order.getMerTxnAmt())) {
					logger.error("手续费金额大于订单金额，中断交易！pay error: feeAmt[" + poundageRes.getTotalFeeAmt() + "]>merTxnAmt["
							+ order.getMerTxnAmt() + "]! " + command + ", " + order);
					throw new WebException(ProcCodeConstants.PROC_CODE_100E5055);
				}
				/*通用分账  added by Chenyu Li at 2018/3/10 start*/
				if(isSplit(order)){
					CpsReceiptSplitDtl cpsReceiptSplitDtl = cpsReceiptSplitDtlQueryDAO.queryPlatformDtlsByGopayOrderId(order.getGopayOrderId());
					if (cpsReceiptSplitDtl == null) {
						logger.error("平台方没有参与分账，中断交易！pay error: feeAmt[" + poundageRes.getTotalFeeAmt() + "]>merTxnAmt["
								+ order.getMerTxnAmt() + "]! " + command + ", " + order);
						throw new WebException(ProcCodeConstants.PROC_CODE_100E1033);
					}
					if (1 == poundageRes.getTotalFeeAmt().compareTo(cpsReceiptSplitDtl.getMerTxnAmt())) {
						logger.error("手续费金额大于平台方分账金额，中断交易！pay error: feeAmt[" + poundageRes.getTotalFeeAmt() + "]>merTxnAmt["
								+ order.getMerTxnAmt() + "]! " + command + ", " + order);
						throw new WebException(ProcCodeConstants.PROC_CODE_100E1405);
					}
				}
				/*通用分账  added by Chenyu Li at 2018/3/10 end*/
//				BigDecimal subAccountAmt = subAccountInfoService.subAccountSumAmt(order);
//				BigDecimal txnAmt = order.getMerTxnAmt().subtract(poundageRes.getTotalFeeAmt());
//				if(subAccountAmt.compareTo(txnAmt) >0){
//					logger.error("分账总金额大于订单金额和手续费金额，中断交易！pay error: feeAmt[" + poundageRes.getTotalFeeAmt() + "]>merTxnAmt["
//							+ order.getMerTxnAmt() + "]! " +"subAccountAmt["+subAccountAmt+"]! " + command + ", " + order);
//					throw new WebException(ProcCodeConstants.PROC_CODE_100E1186);
//				}
			}
		}

		try {
			logger.error("调用cps更新订单状态.BEGIN.call cps update mainOrder." + command + ", " + order + ", " + values);
			mainOrderUpdateService.updateMany(order.getGopayOrderId(), values);
			logger.error("调用cps更新订单状态.END.call cps update mainOrder." + command + ", " + order);
		} catch (GopayException e) {
			logger.error("调用cps更新订单状态出错！call cps update mainOrder exception!" + command + ", " + order, e);
			throw new WebException(e.getErrCode());
		}
	}

	/**
	 * 扫码支付 清算渠道方案校验.
	 *
	 * @param command
	 * @param order
	 * @throws WebException
	 */
	protected void checkSolution(CpsGenMainOrder order, ScanPayCommand command) throws WebException {
		/*通用分账  added by Chenyu Li at 2018/3/13 start*/
		PayChannelWayBean payWay;
		if(isSplit(order)){
			payWay = soluBisPayChannelService.getSoluBisPayChannel(order.getGopayIntTxnCd(), order.getOtherAcct());
		}else{
			payWay = soluBisPayChannelService.getSoluBisPayChannel(order.getGopayIntTxnCd(), getAcctId(order));
		}
		/*通用分账  added by Chenyu Li at 2018/3/13 end*/
		// 方案清算渠道校验
		String payChannel = PayChannel._16.value;
		boolean isCheck = false;
		if (payWay.isSupport(PayChannelType.SCAN_PAY.value)) {
			String bankCode = command.getBankCode();
			PayChannelDtlBean payChannelDtl = payWay.getPayChannelDtl(PayChannelType.SCAN_PAY.value);
			Map<String, List<PayChannelDtlInfo>> bankCodes = payChannelDtl.getBankCodes();
			if (bankCodes != null && bankCodes.size() > 0) {
				List<PayChannelDtlInfo> list = bankCodes.get(payChannel);
				if (list != null && list.size() > 0) {
					for (PayChannelDtlInfo payChannelDtlInfo : list) {
						if (org.apache.commons.lang3.StringUtils.equals(payChannelDtlInfo.getOutStlmId(), bankCode)) {
							isCheck = true;
							break;
						}
					}
				}
			}
		}
		if (!isCheck) {
			logger.error("扫码支付方案校验未通过orderId=" + order.getGopayOrderId());
			throw new WebException(ProcCodeConstants.PROC_CODE_100E6085);
		} else {
			logger.error("扫码支付方案校验通过orderId=" + order.getGopayOrderId());
		}
	}

    /**
     * 扫码支付 结果页面
     *
     * @param orderId
     * @return
     * @throws WebException
     */
    @RequestMapping(value = "/result")
    public ModelAndView expressResult(String orderId) throws WebException {
        CpsGenMainOrder order = cpsGeneralMainOrderQueryDAO.find(CpsGenMainOrder.class, orderId);
        if (order == null) {
			throw new WebException(ProcCodeConstants.PROC_CODE_100E5041);
		}
        if (TxnStaCode.TXN_STA_CD_30000.value == (order.getTxnStaCd())) {
            logger.error("订单状态不对！txn_sta_cd wrong!" + orderId);
            throw new WebException(ProcCodeConstants.PROC_CODE_100E5042);
        }
        ModelAndView mv = new ModelAndView();
        mv.addObject("portalUrl", PORTAL_URL);
        if (TxnStaCode.TXN_STA_CD_20000.value == order.getTxnStaCd()) {
            mv.setViewName("/00100/common/success");
        } else {
            mv.setViewName("/00100/common/fail");
        }
        mv.addObject("order", getOrderInfo(orderId));
        mv.addObject("notifyMerUrl", "/redirect/BankPayCallbackSelf.shtml?orderKey=" + getRequest().getParameter("orderKey") + "&orderId=" + orderId);
        logOut();
        return mv;
    }
}
